﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Bettzueche.RLGlue.Generics;
using Bettzueche.RLGlue.RLTypes;

namespace Bettzueche.RLGlue.RLComponent {

    public interface IAgent : AnAgent {

        /// <summary>
        /// Initialisiert vom Agenten benötigte Datenstrukturen wie z.B. ein Value-Function Array.<para>
        /// Die task_spec kann zur Parametriesierung genutzt werden.</para>
        /// </summary>
        /// <param name="task_spec">Task Spezifikation: ASCII codierte Beschreibung des Experiments und der Umwelt; 
        /// Eventuell leerer String.</param>
        /// <remarks>
        /// Agent_init wird zu Beginn eines Experiments aufgerufen.<para>
        /// Üblicherweise parst Agent_init die Task Spezifikation und speichert verschiedene, in diesem String kodierte,
        /// Informationen. Zum Beispiel initialisiert Agent_init nach parsen der task_spec das Value-Function Array entsprechend
        /// der Dimension des Zustandsraums, die in "number of states" in der task_spec steht.</para><para>
        /// Bedenken Sie jedoch, dass die task_spec nicht zwingend erforderlich ist und lediglich ein leerer String sein kann.</para>
        /// </remarks>
        void InitAgent(String task_spec);


        /// <summary>
        /// Liefert abhängig vom gegebenen Startzustand die erste auszuführende Aktion.
        /// </summary>
        /// <param name="observation">Startzustand</param>
        /// <returns>Die vom Agenten gewählte nächste (erste) Aktion</returns>
        /// <remarks>
        /// Die Agent_start Methode wählt die erste Aktion zu Beginn einer Episode basierend auf den ersten Beobachtungen
        /// (Zustand) der Umwelt. Agent_start erhält keine Belohnung als Input; Agent_start beinhaltet normalerweise
        /// keinen Lern-Aktualisierungs-Code. Die folgende beispielhafte Implemtierung wählt die erste Aktion basierend
        /// auf den aktuellen Schätzwert V (Value-Function):
        /// <code>
        /// 1. agent_start (observation) --> action
        /// 2.      lastObservation=observation
        /// 3.      for each action a
        /// 4.           if highest valued action valueFunction(observation,a)
        /// 5.           then store a as lastAction
        /// 6. return lastAction
        /// </code>
        /// </remarks>
        GeneralAction StartAgent(GeneralState observation);


        /// <summary>
        /// Lernalgorithmus des Agenten. <para>
        /// Auf Basis der aktuellen Position und Beobachtung (Zustand) erfolgt das "Lernen"
        /// und eine FolgeAktion wird zurückgegeben.</para>
        /// </summary>
        /// <remarks>
        /// <para>
        /// Die Agent_step Methode implementiert das Herz des Lernagorithmus und Aktionsauswahlmechanismus.
        /// Diese Methode muss mindestens eine Aktion liefern, jedes mal wenn sie Aufegerufen wird.
        /// Bei den meisten lernenden Agenten fragt die Step-Methode die Aktionsauswahl-Funktion ab und führt eine
        /// auf die eingegebene Beobachtung und Belohnung basierende Lernaktualisierung aus.</para><para>
        /// Der folgende Agent_step Funktion zeigt Pseudocode für einen Value-Update:</para>
        /// <code>
        /// 1. agent_step(reward, observation)-> action
        /// 2.      update(valueFunction, lastObservation, lastAction, reward, observation)
        /// 3.      newAction = select_action(observation, valueFunction)
        /// 4.      lastObservation = observation
        /// 5.      lastAction = newAction
        /// 6. return newAction
        /// </code>
        /// <para>
        /// Beachten Sie, dass das Agentenprogramm expliziet den Zustand und die Aktion des vorigen Schrittes speichern muss.
        /// RL-Glue stellt nicht die Historie der Aktionen, Zustände und Belohnungen für den Agenten oder die Umwelt zu Verfügung.
        /// </para>
        /// </remarks>
        /// <param name="reward">aktuelle Belohnung (der zuletzt ausgeführten Aktion)</param>
        /// <param name="observation">aktueller Zustand (der durch die letzte Aktion erreicht wurde)</param>
        /// <returns>Folgeaktion, die der Agent gemäß seiner Strategie als nächsten ausführen möchte.</returns>
        GeneralAction StepAgent(double reward, GeneralState observation);


        /// <summary>
        /// Markiert das Ende einer Episode und übergibt die letzte Belohnung.<para>
        /// Wird üblicherweise von RL-Glue aufgerufen.</para>
        /// </summary>
        /// <param name="reward">Belohnung der letzten und finalen Aktion</param>
        /// <remarks>
        /// <para>
        /// In episodischen Aufgaben geht die Umwelt in einen abschließenden Zustand über, der die Episode beendet.
        /// RL-Glue erwidert das Ende einer Episode durch Aufruf der Agent_end Methode, übergibt die Belohnung der letzten
        /// Transition und signalisiert das Ende der aktuellen Epsiode.
        /// </para><para>
        /// Normalerweise führt die Agent_end Methode abschließende Lernaktualisierungen aus, basierend auf der letzten Transition
        /// und vollzieht ebenfalls andere Episodenabschluss Routinen, wie Löschen der Eligibility Traces.
        /// </para><para>
        /// Ist die Umwelt nichtepisodisch, wird RL-Gue nie Agent_end aufrufen. 
        /// </para>
        /// </remarks>
        void EndAgent(double reward);


        /// <summary>
        /// Gibt interne Ressourcen frei.<para>
        /// Wird üblicherweise am Ende eines Lernexperiments aufgerufen.</para>
        /// </summary>
        void CleanupAgent();


        /// <summary>
        /// Kommunikation mit dem Agenten, um bspw. Parameter zu setzen oder Werte abzufragen.
        /// </summary>
        /// <param name="message">Nachricht entsprechend Ihres Protokolls</param>
        /// <returns>Antwort auf eine in message codierte Anfrage</returns>
        /// <remarks>
        /// Die agent_message Funktion wird verwendet, um eine beliebige String Nachricht an das Agent-Programm zu senden.<para>
        /// Diese Funktion kann z.B. verwendet werden, um Parameter des Agenten zu ändern, ihm mitzuteilen, dass die Explorations-Phase
        /// vorbei ist oder den Namen des Agenten-Programm anfordern. Meinche haben ganze Protokolle erzeugt, die Agent_message
        /// nutzen, um Lernparameter des Agenten zu setzen, deren Value-Function abzufragen und vieles mehr.</para><para>
        /// Kurzbeispiel, dass Parameter abfragt:</para>
        /// <code>
        /// agent_message(inMessage) --> outMessage
        ///     if inMessage == "getCurrentStepSize"  
        ///         return alpha
        ///     end 
        ///     if inMessage == "getCurrentExplorationRate"  
        ///         return epsilon
        ///     end 
        /// return ""
        /// </code>
        /// </remarks>
        String MessageAgent(String message);
    }
}
